import React, { useState, useRef, useEffect } from "react";
import classNames from "classnames";
import { CSSTransition } from "react-transition-group";
import Input, { InputProps } from "../Input/input";

/**
 * 泛型建议项类型
 * DataSourceType<T> 将组件内部要求的 value:string 与外部传入的业务字段 T 合并
 */
export interface DataSourceType<T = {}> {
  value: string;
  data?: T;
}

/**
 * AutoComplete 组件属性类型
 */
export interface AutoCompleteProps extends Omit<InputProps, "onChange" | "value" | "onSelect"> {
  /**
   * 输入框的受控值（可选）。如果提供则作为受控组件使用。
   */
  value?: string;
  /**
   * 建议数据获取函数，支持同步数组或 Promise 异步返回
   */
  fetchSuggestions: (query: string) => DataSourceType[] | Promise<DataSourceType[]>;
  /**
   * 选中某项的回调
   */
  onSelect?: (item: DataSourceType) => void;
  /**
   * 自定义建议项的渲染模板
   */
  renderOption?: (item: DataSourceType) => React.ReactElement;
  /**
   * 输入变化回调（向外暴露原始字符串）
   */
  onChange?: (value: string) => void;
  /**
   * 输入值防抖时间（毫秒）
   */
  debounce?: number;
  /**
   * 空态提示文本
   */
  emptyText?: string;
}

/**
 * 防抖 Hook：在 delay 毫秒后返回稳定的值。
 * @param value 需要防抖的值
 * @param delay 毫秒延迟
 */
function useDebounce<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const timer = window.setTimeout(() => setDebounced(value), delay);
    return () => window.clearTimeout(timer);
  }, [value, delay]);
  return debounced as T;
}

/**
 * 点击组件外部关闭的 Hook
 * @param ref 目标 Ref
 * @param handler 触发关闭的处理函数
 */
function useClickOutside<T extends HTMLElement>(ref: React.MutableRefObject<T | null>, handler: () => void) {
  useEffect(() => {
    const listener = (e: MouseEvent) => {
      const el = ref.current;
      if (!el || el.contains(e.target as Node)) return;
      handler();
    };
    document.addEventListener("mousedown", listener);
    return () => {
      document.removeEventListener("mousedown", listener);
    };
  }, [ref, handler]);
}

/**
 * AutoComplete 组件
 * - 支持同步/异步建议
 * - 键盘导航（上下键高亮、回车选中、ESC 关闭）
 * - 自定义模板渲染
 */
export const AutoComplete: React.FC<AutoCompleteProps> = (props) => {
  const {
    className,
    placeholder,
    disabled,
    value: controlledValue,
    fetchSuggestions,
    onSelect,
    renderOption,
    onChange,
    debounce = 300,
    emptyText = "无匹配项",
    ...restInputProps
  } = props;

  // 内部非受控值
  const [inputValue, setInputValue] = useState(controlledValue ?? "");
  // 建议列表
  const [suggestions, setSuggestions] = useState<DataSourceType[]>([]);
  // 是否展示下拉
  const [showDropdown, setShowDropdown] = useState(false);
  // 高亮索引
  const [highlightIndex, setHighlightIndex] = useState(-1);
  // 加载态
  const [loading, setLoading] = useState(false);

  const containerRef = useRef<HTMLDivElement>(null);
  useClickOutside(containerRef, () => setShowDropdown(false));

  // 受控同步
  useEffect(() => {
    if (controlledValue !== undefined) {
      setInputValue(controlledValue);
    }
  }, [controlledValue]);

  // 防抖后的查询值
  const debouncedValue = useDebounce(inputValue, debounce);

  /**
   * 拉取建议数据，支持同步与异步
   */
  useEffect(() => {
    const query = debouncedValue.trim();
    if (!query) {
      setSuggestions([]);
      setShowDropdown(false);
      setLoading(false);
      return;
    }
    // 查询非空：开启下拉并显示加载态
    setShowDropdown(true);
    setLoading(true);
    const result = fetchSuggestions(query);
    if (result instanceof Promise) {
      result
        .then((list) => {
          setSuggestions(list || []);
          // 保持下拉打开，若无数据显示空态
          setShowDropdown(true);
        })
        .finally(() => setLoading(false));
    } else {
      const list = result || [];
      setSuggestions(list);
      // 保持下拉打开，若无数据显示空态
      setShowDropdown(true);
      setLoading(false);
    }
    setHighlightIndex(-1);
  }, [debouncedValue, fetchSuggestions]);

  /**
   * 处理输入变化
   * @param e 输入事件
   */
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const val = e.target.value;
    if (controlledValue === undefined) setInputValue(val);
    onChange?.(val);
  };

  /**
   * 选中建议项
   * @param item 被选中的建议项
   */
  const handleSelect = (item: DataSourceType) => {
    if (controlledValue === undefined) setInputValue(item.value);
    setShowDropdown(false);
    onSelect?.(item);
  };

  /**
   * 键盘导航与选择
   */
  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    switch (e.key) {
      case "ArrowDown":
        e.preventDefault();
        setHighlightIndex((prev) => {
          const next = Math.min(prev + 1, suggestions.length - 1);
          return next;
        });
        break;
      case "ArrowUp":
        e.preventDefault();
        setHighlightIndex((prev) => Math.max(prev - 1, 0));
        break;
      case "Enter":
        if (highlightIndex >= 0 && highlightIndex < suggestions.length) {
          handleSelect(suggestions[highlightIndex]);
        }
        break;
      case "Escape":
        setShowDropdown(false);
        break;
      default:
        break;
    }
  };

  /**
   * 渲染单个建议项
   * @param item 建议项
   */
  const renderItem = (item: DataSourceType, index: number) => {
    const isActive = index === highlightIndex;
    const classes = classNames("viking-auto-complete-item", {
      "is-active": isActive,
    });
    return (
      <li key={index} className={classes} onMouseDown={() => handleSelect(item)}>
        {renderOption ? renderOption(item) : item.value}
      </li>
    );
  };

  const wrapperCls = classNames("viking-auto-complete", className);

  // 过渡组件节点引用，避免使用 findDOMNode
  const dropdownRef = useRef<HTMLUListElement>(null);

  return (
    <div className={wrapperCls} ref={containerRef}>
      <Input
        placeholder={placeholder}
        disabled={disabled}
        value={inputValue}
        onChange={handleChange}
        onKeyDown={handleKeyDown}
        {...restInputProps}
      />
      <CSSTransition in={showDropdown} timeout={200} classNames="zoom-in-top" unmountOnExit nodeRef={dropdownRef}>
        <ul className="viking-auto-complete-dropdown" ref={dropdownRef}>
          {loading && (
            <li className="viking-auto-complete-loading">加载中...</li>
          )}
          {!loading && suggestions.length === 0 && (
            <li className="viking-auto-complete-empty">{emptyText}</li>
          )}
          {!loading && suggestions.length > 0 && suggestions.map(renderItem)}
        </ul>
      </CSSTransition>
    </div>
  );
};

export default AutoComplete;